home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Text⁄Files / Suntar 1.3.2 / semimodal.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-10-18  |  11.8 KB  |  372 lines  |  [TEXT/KAHL]

  1. /*******************************************************************************\
  2.  
  3. movable modal dialog module
  4. © Gabriele Speranza  1-5 May 1991
  5.  modified on 26-27 Dec 1991 in order to use a resource
  6.  and some other changes later
  7.  
  8. This program is public domain, feel free to use it or part of it for anything
  9.  
  10. \*******************************************************************************/
  11. #include "antiglue.h"
  12. #include "windows.h"
  13. #include "suntar.h"
  14.  
  15. /* I know that Apple has specified its rules about movable modal dialogs,
  16. but this module was written before I've seen System 7.0 or read about
  17. movable modal dialogs, hence I did it according to my personal taste and 
  18. gave it the name I felt right for it. Maybe later I'll remove those little
  19. incompatibilities with Apple guidelines.
  20. Anyway, it works with dialogs having WDEF 5 rather than 3 (it does NOT work
  21. with WDEF 1, since MultiFinder does not send an application to background
  22. when the front window has WDEF 1), with the only anomaly that the dialog
  23. may be dragged also from any point in its content, not only from the title
  24. bar
  25. */
  26.  
  27.  
  28. static WindowRecord    *sm_win_ptr;
  29. static Rect my_text_rect;
  30. static unsigned char** my_text;
  31. static short my_text_just;
  32. static Boolean my_has_default;
  33. Boolean finestra_sm_aperta=false;
  34. Boolean sm_checkbox_status;
  35.  
  36. #define noItem 100
  37.  
  38. short new_sm_evf(EventRecord *);
  39. static short new_sm_evf(theEvent)
  40. register EventRecord *theEvent;
  41. {
  42. /* internal event filter: it stops all events which are trying to bring
  43. to front a window which is not the semimodal dialog, and handles events
  44. regarding the window
  45. returns:
  46. 0: the event was not handled
  47. noItem: the event was handled
  48. anything else: semimodalDialog must close the window and return this code to the caller
  49. */
  50. short                code;
  51. WindowPtr        whichWindow;
  52. Point pt=theEvent->where;
  53.  
  54. switch(theEvent->what){
  55. case mouseDown:
  56.     code=FindWindow( theEvent->where, &whichWindow );
  57.     if(code==inMenuBar||code==inDesk||code==inSysWindow)
  58.         ;
  59.     else if(code==inDrag && whichWindow!=(WindowPtr)sm_win_ptr && 
  60.         (theEvent->modifiers&cmdKey))
  61.         ;        /* il tasto command comporta il non rendere corrente la finestra
  62.         -- accept a command-drag of another window
  63.          */
  64.     else if(whichWindow==(WindowPtr)sm_win_ptr){
  65.     /* inutile testare code, non può essere che inContent, non ha altre aree */
  66.         GrafPtr    savePort;
  67.         ControlHandle     theControl;
  68.         short item;
  69.         GetPort( &savePort );
  70.         SetPort( sm_win_ptr );
  71.         GlobalToLocal( &pt );
  72.         if ((FindControl(pt, whichWindow, &theControl)) != 0) {
  73.             /*code=TrackControl(theControl, theEvent->where, 0L);
  74.             if(code) controlHit=theControl;     a button was pushed */
  75.             if(DialogSelect(theEvent,&whichWindow,&item)){
  76.                 /* if you want to add an hook to the parameters of semimodalDialog,
  77.                 so that it becomes as much customizable as ModalDialog, it should
  78.                 be called here */
  79.                 short    kind;
  80.                 Handle    h;
  81.                 Rect    r;
  82.                 GetDItem(sm_win_ptr,item,&kind,&h,&r);
  83.                 if( (kind&0x7F)==ctrlItem+chkCtrl ){
  84.                     SetCtlValue((ControlHandle)h, (sm_checkbox_status=!sm_checkbox_status) );
  85.                     return noItem;
  86.                     }
  87.                 else
  88.                     return item;
  89.                 }
  90.             }
  91.         else{    /* nel resto della finestra => la sposto
  92.             -- the semimodal dialog is moved by clicking on any point inside it
  93.             but outside the buttons, and dragging
  94.             */
  95.                     Rect    dragRect;
  96.                     dragRect.left=0;
  97.                     dragRect.top=MBARHEIGHT;
  98.                     dragRect.right=screenBits.bounds.right;
  99.                     dragRect.bottom=screenBits.bounds.bottom;
  100.                     InsetRect(&dragRect,20,10);
  101.                     DragWindow( sm_win_ptr, theEvent->where, &dragRect );
  102.                 }
  103.         SetPort( savePort );
  104.         return noItem;
  105.         }
  106.     else{
  107.         WindowPtr wp;
  108.         wp=FrontWindow();
  109.         if(wp!=NULL && ((WindowPeek)wp)->windowKind<userKind){
  110.         /* allora il click sulla console va reinterpretato come un click sulla finestrina
  111.         di dialogo e non va affatto ignorato
  112.         -- I've got a click on the console when the current window belonged to a desk
  113.         accessory or another task: bring to front the semimodal dialog
  114.         */
  115.             SelectWindow(sm_win_ptr);
  116.             }
  117.         return noItem;
  118.         }
  119.     break;
  120. case keyDown:
  121.     if(my_has_default && ((unsigned char)theEvent->message==CR || 
  122.                         (unsigned char)theEvent->message==enter_key) ){
  123.         short    kind;
  124.         Handle    h;
  125.         Rect    r;
  126.         GetDItem(sm_win_ptr,1,&kind,&h,&r);
  127.         SelectButton(h);
  128.         return 1;
  129.         }
  130.  
  131. }    /* fine switch */
  132. return 0;
  133. }
  134.  
  135. static void semimodal_updater(EventRecord*);
  136. static void semimodal_updater(theEvent)
  137. EventRecord*theEvent;
  138. {
  139. WindowPtr w=NULL;
  140. short item;
  141. DialogSelect(theEvent,&w,&item);
  142.  
  143. if(my_has_default)
  144.     {short    kind;
  145.     Handle    h;
  146.     Rect    r;
  147.     GetDItem(sm_win_ptr,1,&kind,&h,&r);
  148.     OutlineControl(h);
  149.     }
  150. }
  151.  
  152. pascal void redrawText(WindowPtr,short);
  153. static pascal void redrawText(theWindow,itemNo)
  154. WindowPtr theWindow;
  155. short itemNo;
  156. {
  157. HLock(my_text);
  158. TextBox ((*my_text)+1,(long)(**my_text),&my_text_rect,my_text_just);
  159. HUnlock(my_text);
  160. }
  161.  
  162. short semimodalDialog(dialog_ID,where,filter,titoli_italiani,n_titoli,p1,p2,p3,
  163.     just,has_default,initializer)
  164. /* the only external entry point: create and handle a semimodal dialog: */
  165. short dialog_ID;
  166. Point *where;    /* NULL for a centered window, a pointer to a point (usually
  167.     initialized to -1,-1) for a window which remembers its old position */
  168. short (*filter)(EventRecord*);    /* an optional event filter to use during the dialog,
  169.         executed before the internal event filter. It must return on of these codes:
  170.         0: that event was not filtered, or was filtered but the standard actions 
  171.             still need be performed
  172.         1: filtered event, don't handle it
  173.         <0: filtered event, you must return immediately with this value as return code,
  174.             the caller knows what to do with that code
  175.         */
  176. char **titoli_italiani;
  177. short n_titoli;
  178. Str255 p1,p2,p3;    /* unfortunately, if I use static text items I have two problems:
  179.     1) suntar 1.0 and 1.1 used two different ways to center the messages,
  180.       with static text items that can't be done: that's bad, but it might
  181.       be tolerated
  182.     2) if a modal dialog appears (and there may be a modal dialog above a
  183.       semimodal...) the ParamText for the modal dialog overwrites the ParamText
  184.       for the semimodal, so that when it's redrawn later its messages are
  185.       changed: horrible !
  186.     Hence, I was obliged to avoid using ParamText. And since I did not want
  187.     to handle updates in a complex way, I had to use a userItem rather than a
  188.     staticText. But this is a bad choice, one day I'll change that
  189.     */
  190. short just;        /* justification, usually centered, but it may be left too */
  191. Boolean has_default;
  192. void (*initializer)(WindowRecord*);        /* The biggest problem with this routine it
  193.     that it's monolithic: unlike with ModalDialog, it's called before
  194.     creating the window and returns after disposing it, so I can't do
  195.     any preprocessing (setting the checkbox value) nor postprocessing
  196.     (reading its final value), and everything must be handled here.
  197.      Probably one day I'll rewrite it so that such things may be done, maybe
  198.     breaking it into three pieces. By now, the most necessary thing is an
  199.     initializer, and here it is */
  200. /* one more parameter, sm_checkbox_status, is a global variable: after calling
  201. semimodalDialog, it contains the final value of a checkbox in the dialog (if it
  202. was correctly initialized): that allows me to avoid the "finalizer" and the dialog hook.
  203. */
  204. {
  205.     AlertTHndl    alertHandle;
  206.     short item;
  207.  
  208.     flush_all();
  209.     check_foreground();
  210.     SetCursor(&arrow);
  211.  
  212.     alertHandle = (AlertTHndl)Get1Resource('DLOG',dialog_ID);
  213.     if (alertHandle) {
  214.         short    kind;
  215.         Handle    h;
  216.         Rect    r;
  217.         short items_in_dialog;
  218.         HLock((Handle)alertHandle);
  219.         if(where==NULL||where->v<0)
  220.             PositionDialog( &((**alertHandle).boundsRect));
  221.         else{
  222.             register Rect *r=&((**alertHandle).boundsRect);
  223.             r->bottom= where->v+(r->bottom-r->top);
  224.             r->right= where->h+(r->right-r->left);
  225.             r->top=where->v;
  226.             r->left=where->h;
  227.             }
  228.         /* (**alertHandle).windowDefProc=(Handle)altDBoxProc; */
  229.         sm_win_ptr=GetNewDialog(dialog_ID,NULL,(WindowPtr)-1L);
  230.         sm_win_ptr->refCon=-1;        /* il -1 
  231.                             serve per non essere considerata 
  232.                             una "mia" finestra dal modulo windows 
  233.                             -- the windows module uses the refCon field to
  234.                             recognize its own windows, hence it can't be 0 */ 
  235.         HUnlock((Handle)alertHandle);
  236.         finestra_sm_aperta=true;
  237.         items_in_dialog= *(short*)*(((DialogRecord*)sm_win_ptr)->items) + 1;
  238.         if(in_Italia && titoli_italiani){
  239.             short fatti=0;
  240.             for(item=1;fatti<n_titoli&&item<=items_in_dialog;item++){
  241.                 GetDItem(sm_win_ptr,item,&kind,&h,&r);
  242.                 if((kind&0x7F)==ctrlItem+btnCtrl || (kind&0x7F)==ctrlItem+chkCtrl){
  243.                     SetCTitle(h,*titoli_italiani++);
  244.                     fatti++;
  245.                     }
  246.                 }
  247.             }
  248.         my_has_default=has_default;
  249.         if(has_default){
  250.             GetDItem(sm_win_ptr,1,&kind,&h,&r);    /* item 1 must be the default button */
  251.             OutlineControl(h);
  252.             }
  253. /*if(p1==NULL) printf("p1=NULL "); else printf("p1=%P",p1);
  254. if(p2==NULL) printf("p2=NULL "); else printf("p2=%P",p2);
  255. if(p3==NULL) printf("p3=NULL "); else printf("p3=%P",p3);*/
  256.         if(p1!=NULL){
  257.             short i;
  258.             item=1;
  259.             do{    /* find the Rect of the useritem (p1,p2,p3): an userItem MUST exist */
  260.                 GetDItem(sm_win_ptr,++item,&kind,&h,&my_text_rect);
  261.                 }
  262.             while((kind&0x7F)!=userItem && item<items_in_dialog);
  263.             if((kind&0x7F)==userItem) SetDItem (sm_win_ptr,item, kind,
  264.                 (Handle)redrawText, &my_text_rect);
  265.                 /* probably I could have used a static text item and SetIText,
  266.                 but then I would have got left justification only */
  267.             i=p1[0]+1;
  268.             if(p2) i+=p2[0];
  269.             if(p3) i+=p3[0];
  270.             my_text=NewHandle(i);
  271.             my_text_just=just;
  272.             pStrcpy(*my_text,p1);
  273.             if(p2)pStrcat(*my_text,p2);
  274.             if(p3)pStrcat(*my_text,p3);
  275.             }
  276.         else
  277.             my_text=NULL;
  278.         if(initializer!=NULL)
  279.             (*initializer)(sm_win_ptr);
  280.         else
  281.             sm_checkbox_status=false;
  282.  
  283.         item=0;
  284.         install_handlers(sm_win_ptr,semimodal_updater,NULL);
  285.         do{
  286.             {
  287.             WindowPeek wp;
  288.             wp= gInBackground ? NULL : (WindowPeek) FrontWindow();
  289.             if(wp!=NULL &&  wp->windowKind>=userKind && wp !=sm_win_ptr)
  290.                 SelectWindow(sm_win_ptr);    /* non dovrebbe mai capitare,ma non fidarsi
  291.                                             può salvare dai guai
  292.                         -- the internal event filter should prevent the console window 
  293.                         from going to front but there are a few utilities which add
  294.                         a "windows" menu to applications and can bring to front any
  295.                         window by cheating. If that happens, bring to front the semimodal
  296.                         dialog again
  297.                         */
  298.             }{
  299.             EventRecord        myEvent;
  300.             if(get_event(&myEvent)){
  301.                 if(filter){
  302.                     if ( (item=(*filter)(&myEvent))==0 )
  303.                         item=new_sm_evf(&myEvent);
  304.                     else if(item>0){
  305.                         GetDItem(sm_win_ptr,item,&kind,&h,&r);
  306.                         if(kind==ctrlItem+btnCtrl)
  307.                             SelectButton(h);
  308.                         }
  309.                     }
  310.                 else
  311.                     item=new_sm_evf(&myEvent);
  312.                 if(item==0){
  313.                     if(my_event_filter!=NULL) (*my_event_filter)(&myEvent);
  314.                     handle_event(&myEvent);
  315.                     if(is_pause_command()){
  316.                         HideWindow(sm_win_ptr);
  317.                         handle_pause();
  318.                         ShowWindow(sm_win_ptr);
  319.                         SelectWindow(sm_win_ptr);
  320.                         }
  321.                     }
  322.                 }
  323.             else{
  324.                 /* the user filter must be called for null events too... */
  325.                 if(filter){
  326.                     myEvent.what=nullEvent;
  327.                     item=(*filter)(&myEvent);
  328.                     }
  329.                 }
  330.             if(item==noItem) item=0;
  331.             }
  332.             accept_abort_command();
  333.             }
  334.         while(item==0);
  335.  
  336.         my_event_filter=NULL;
  337.  
  338.         if(where){
  339.             GrafPtr    savePort;
  340.             GetPort( &savePort );
  341.             SetPort( sm_win_ptr );
  342.             *where=*(Point*)&(sm_win_ptr->port.portRect);    /* top e left */
  343.             LocalToGlobal(where);        /* remember where the window was before closing it*/
  344.             SetPort(savePort);
  345.             }
  346.     
  347.         DisposDialog(sm_win_ptr);
  348.         remove_handlers(sm_win_ptr);
  349.         if(my_text)DisposHandle(my_text);
  350.         finestra_sm_aperta=false;
  351.         return item;
  352.         }
  353. }
  354.  
  355.  
  356. void close_semimodal()
  357. {    /* viene chiamata in caso di un longjmp che potrebbe abortire la routine precedente
  358. -- in suntar, all files or windows which may be open when the abort command is enabled
  359. or when an error may occur, must provide a "cleanup" routine to close everything (except 
  360. when their creator has a setjmp and performs directly the cleanup).
  361. Don't use for any other purpose
  362. */
  363. if(finestra_sm_aperta){
  364.     DisposDialog(sm_win_ptr);
  365.     remove_handlers(sm_win_ptr);
  366.     if(my_text)DisposHandle(my_text);
  367.     finestra_sm_aperta=false;
  368.     }
  369. }
  370.  
  371.  
  372.